﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DbSharp.SupportLib.Crypto;
using System.Xml.Linq;
using System.Globalization;
using Microsoft.Win32;


namespace DbSharp.Framework.ReleaseManage
{
    interface IEvaluationTrackService
    {
        DateTime GetEvaluationFirstTime();
        bool TryToSetExpired();
    }


    class EvaluationTrackFile : IEvaluationTrackService
    {
        const string m_DateFmt = "yyyy-MM-dd hh:mm";
        
        const string m_TrialXmlFmt = @"<?xml version=""1.0"" encoding=""utf-8"" ?>             
<t>
<fg>{0}</fg>
<dt>{1}</dt>
</t>
";

        private SymmetricCryptoService m_Crypto = new SymmetricCryptoService();

        private bool m_HaveAdminPermission;

        private DateTime m_MachineOtherUserFirstEvalDate = DateTime.MinValue;


        public EvaluationTrackFile()
        {
            string commonTrackFileName = @"Microsoft\Internet Explorer\MSIMGSIZAKDSJ567.DAT";
            List<string> listPathSection = new List<string>();
            listPathSection.Add("Microsoft");
            listPathSection.Add("Internet Explorer");
            string testFileName = "MSIMGSIZAKDSJ500.DAT";
            string commonAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            m_HaveAdminPermission = HaveWritePermission(commonAppDataPath, listPathSection, testFileName);
            commonTrackFileName=Path.Combine(commonAppDataPath,commonTrackFileName) ;

            //if have no administrator permission, we need to create the necessary directory in LocalApplicationData folder.
            if (m_HaveAdminPermission == false)
            {
                //Create folder for local track file
                string localAppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                HaveWritePermission(localAppDataPath, listPathSection, testFileName);


                //get first evaluation date of other user on this machine
                try
                {
                    if (File.Exists(commonTrackFileName))
                        m_MachineOtherUserFirstEvalDate = _GetEvaluationFirstTimeFromFile(commonTrackFileName);
                }
                catch
                {
                    m_MachineOtherUserFirstEvalDate = DateTime.MinValue;
                }
            }
        }

        private string Decrypt(string base64SecretMsg)
        {
            string secretText = base64SecretMsg;
            string plainText = m_Crypto.Decrypto(secretText);
            return plainText;
        }


        private string Encrypt(string base64PlainMsg)
        {
            string plainText = base64PlainMsg;
            string secretText = m_Crypto.Encrypto(plainText);
            return secretText;
        }


        /// <summary>
        /// identify whether the logon user have write permission in specific folder
        /// </summary>
        /// <param name="rootPath"></param>
        /// <param name="listPathSection"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool HaveWritePermission(string rootPath, List<string> listPathSection, string fileName)
        {
            try
            {
                string workPath = rootPath;
                foreach (var item in listPathSection)
                {
                    workPath = Path.Combine(workPath, item);
                    if (Directory.Exists(workPath) == false)
                        Directory.CreateDirectory(workPath);
                }
                workPath = Path.Combine(workPath, fileName);
                File.WriteAllText(workPath, Guid.NewGuid().ToString());

                return true;
            }
            catch
            {
                return false;
            }
        }

        private String GetWorkingTrackFilePath()
        {
            string trackFileName = @"Microsoft\Internet Explorer\MSIMGSIZAKDSJ567.DAT";

            string path;
            if (m_HaveAdminPermission)
                path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            else
                path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

            path = Path.Combine(path, trackFileName);
            return path;
        }


        private bool CreateInitialWorkingTrackFile()
        { 
            string today= DateTime.Now.ToString(m_DateFmt);
            string trialXml = string.Format(m_TrialXmlFmt, 0, today);
            return UpdateWorkingTrackFile(trialXml);
        }


        private bool CreateInitialWorkingTrackReg()
        {
            string today = DateTime.Now.ToString(m_DateFmt);
            string trialXml = string.Format(m_TrialXmlFmt, 0, today);
            return UpdateWorkingTrackReg(trialXml);
        }

        private bool GetTrackFileContent(string trackFile, out string plainXml)
        {
            try
            {
                string secretXml = File.ReadAllText(trackFile);
                plainXml = Decrypt(secretXml);
                return true;
            }
            catch
            {
                plainXml = string.Empty;
                return false;
            }
        }


        private bool GetTrackRegContent(out string plainXml)
        {
            try
            {

                //HKEY_CURRENT_USER\Software\Intel
                RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Intel");
                if (key == null)
                    key = Registry.CurrentUser.CreateSubKey("Software\\Intel");

                key = Registry.CurrentUser.OpenSubKey("Software\\Intel\\Masterboard");
                if (key == null)
                    key = Registry.CurrentUser.CreateSubKey("Software\\Intel\\Masterboard");

                key = Registry.CurrentUser.OpenSubKey("Software\\Intel\\Masterboard", false);

                string secretXml =(string) key.GetValue("LongSerialNumber", string.Empty);
                plainXml = Decrypt(secretXml);
                return true;
            }
            catch
            {
                plainXml = string.Empty;
                return false;
            }
        }

        private bool UpdateWorkingTrackFile(string plainXml)
        {
            try
            {
                byte[] bytesIn = System.Text.Encoding.ASCII.GetBytes(plainXml);
                string base64 = Convert.ToBase64String(bytesIn);
                string secretBase64 = m_Crypto.Encrypto(base64);
                File.WriteAllText(GetWorkingTrackFilePath(), secretBase64);
                return true;
            }
            catch
            {
                return false;
            }
        }


        private bool UpdateWorkingTrackReg(string plainXml)
        {
            try
            {
                //HKEY_CURRENT_USER\Software\Intel
                RegistryKey key = Registry.CurrentUser.OpenSubKey( "Software\\Intel");
                if (key==null)
                    key = Registry.CurrentUser.CreateSubKey("Software\\Intel");

                key = Registry.CurrentUser.OpenSubKey( "Software\\Intel\\Masterboard");
                if (key==null)
                    key = Registry.CurrentUser.CreateSubKey( "Software\\Intel\\Masterboard");

                key = Registry.CurrentUser.OpenSubKey("Software\\Intel\\Masterboard", true);
                

                byte[] bytesIn = System.Text.Encoding.ASCII.GetBytes(plainXml);
                string base64 = Convert.ToBase64String(bytesIn);
                string secretBase64 = m_Crypto.Encrypto(base64);

                key.SetValue("LongSerialNumber", secretBase64, RegistryValueKind.String);
                return true;
            }
            catch
            {
                return false;
            }
        }


        #region IEvaluationTrackService Members

        public DateTime GetEvaluationFirstTime()
        {
            try
            {
                DateTime evalDate;
                string trackFile = GetWorkingTrackFilePath();
                evalDate = _GetEvaluationFirstTimeFromFile(trackFile);

                //check the eariest evaluation date on this machine including other user's evaluation
                if (m_MachineOtherUserFirstEvalDate.Equals(DateTime.MinValue) == false)
                { 
                    if (m_MachineOtherUserFirstEvalDate.CompareTo(evalDate) < 0)
                        evalDate = m_MachineOtherUserFirstEvalDate;
                }


                //check the first evaluation date stored in reg
                DateTime evalDate2 = _GetEvaluationFirstTimeFromReg();
                if (evalDate2.CompareTo(evalDate) < 0)
                    evalDate = evalDate2;

                return evalDate;
            }
            catch
            {
                return DateTime.Now.AddYears(-5);
            }
        }

        private DateTime _GetEvaluationFirstTimeFromReg()
        {
            RegistryKey key = Registry.CurrentUser.CreateSubKey("Software\\Intel\\Masterboard");
            if (string.Equals((string)key.GetValue("LongSerialNumber", "-100A"), "-100A", StringComparison.Ordinal))
            {
                CreateInitialWorkingTrackReg();
            }

            string xml;
            bool canOpen = GetTrackRegContent(out xml);
            if (canOpen == false)
            {
                _TryToSetExpiredReg();
                return DateTime.Now.AddYears(-5);
            }
            else
            {
                var xRoot = XElement.Parse(xml);
                if (xRoot.Element("fg").Value.Equals("0", StringComparison.Ordinal))
                {
                    string dateString = xRoot.Element("dt").Value;
                    var styles = DateTimeStyles.AllowWhiteSpaces;
                    DateTime evaluateFirstTime;
                    if (DateTime.TryParseExact(dateString, m_DateFmt, null, styles, out evaluateFirstTime) == false)
                    {
                        return DateTime.Now.AddYears(-5);
                    }
                    else
                    {
                        if (evaluateFirstTime.Date.CompareTo(DateTime.Now.Date) <= 0)
                            return evaluateFirstTime;
                        else
                            return DateTime.Now.AddYears(-5);
                    }
                }
                else
                {
                    return DateTime.Now.AddYears(-5);
                }

            }
        }


        private DateTime _GetEvaluationFirstTimeFromFile(string trackFile)
        {
            if (File.Exists(trackFile) == false)
                CreateInitialWorkingTrackFile();

            string xml;
            bool canOpen = GetTrackFileContent(trackFile,out xml);
            if (canOpen == false)
            {
                _TryToSetExpiredFile();
                return DateTime.Now.AddYears(-5);
            }
            else
            {
               var xRoot= XElement.Parse(xml);
               if (xRoot.Element("fg").Value.Equals("0", StringComparison.Ordinal))
               {
                   string dateString = xRoot.Element("dt").Value;
                   var styles = DateTimeStyles.AllowWhiteSpaces;
                   DateTime evaluateFirstTime;
                   if (DateTime.TryParseExact(dateString, m_DateFmt, null, styles, out evaluateFirstTime) == false)
                   {
                       return DateTime.Now.AddYears(-5);
                   }
                   else
                   {
                       if (evaluateFirstTime.Date.CompareTo(DateTime.Now.Date) <= 0)
                           return evaluateFirstTime;
                       else
                           return DateTime.Now.AddYears(-5);
                   }
               }
               else
               {
                   return DateTime.Now.AddYears(-5);
               }

            }
        }




        public bool _TryToSetExpiredReg()
        { 
            string xml;
            bool canOpen = GetTrackRegContent(out xml);
            if (canOpen == false)
            { 
                string evaluateDateStr = DateTime.Now.AddYears(-5).ToString(m_DateFmt);
                string trialXml = string.Format(m_TrialXmlFmt, 1, evaluateDateStr);
                return UpdateWorkingTrackReg(trialXml);
            }
            else
            { 
                var xRoot = XElement.Parse(xml);
                string dateString = xRoot.Element("dt").Value;
                var styles = DateTimeStyles.AllowWhiteSpaces;
                DateTime evaluateFirstTime;
                if (DateTime.TryParseExact(dateString, m_DateFmt, null, styles, out evaluateFirstTime) == false)
                {
                    evaluateFirstTime= DateTime.Now.AddYears(-5);
                }

                string evaluateDateStr = DateTime.Now.ToString(m_DateFmt);
                string trialXml = string.Format(m_TrialXmlFmt, 1, evaluateDateStr);
                return UpdateWorkingTrackReg(trialXml);
            }
        }

        public bool _TryToSetExpiredFile()
        {
            string trackFile = GetWorkingTrackFilePath();
            if (File.Exists(trackFile) == false)
                CreateInitialWorkingTrackFile();

            string xml;
            bool canOpen = GetTrackFileContent(trackFile, out xml);
            if (canOpen == false)
            {
                string evaluateDateStr = DateTime.Now.AddYears(-5).ToString(m_DateFmt);
                string trialXml = string.Format(m_TrialXmlFmt, 1, evaluateDateStr);
                return UpdateWorkingTrackFile(trialXml);
            }
            else
            {
                var xRoot = XElement.Parse(xml);
                string dateString = xRoot.Element("dt").Value;
                var styles = DateTimeStyles.AllowWhiteSpaces;
                DateTime evaluateFirstTime;
                if (DateTime.TryParseExact(dateString, m_DateFmt, null, styles, out evaluateFirstTime) == false)
                {
                    evaluateFirstTime = DateTime.Now.AddYears(-5);
                }

                string evaluateDateStr = DateTime.Now.ToString(m_DateFmt);
                string trialXml = string.Format(m_TrialXmlFmt, 1, evaluateDateStr);
                return UpdateWorkingTrackFile(trialXml);
            }
        }

        public bool TryToSetExpired()
        {
            try
            {
                return (_TryToSetExpiredFile() && _TryToSetExpiredReg());
            }
            catch
            {
                return false;
            }
        
        }

        #endregion
    }
}
